<!DOCTYPE html>
<html>

<head>
    <title>osm buildings demo</title>
    <script type="text/javascript" src="https://unpkg.com/dat.gui@0.7.6/build/dat.gui.min.js"></script>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
    <script type="text/javascript" src="https://unpkg.com/maptalks/dist/maptalks.js"></script>
    <script type="text/javascript" src="https://unpkg.com/three@0.138.0/build/three.min.js"></script>
    <script type="text/javascript"
        src="https://unpkg.com/maptalks.three@latest/dist/maptalks.three.js"></script>
    <script type="text/javascript"
        src="https://unpkg.com/three@0.138.0/examples/js/libs/stats.min.js"></script>
    <script type="text/javascript" src="buildings.js"></script>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
            width: 100%;
        }

        #map {
            width: 100%;
            height: 100%;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>

        var map = new maptalks.Map("map", {
            center: [13.414049129456203, 52.53160987202452],
            zoom: 17,
            pitch: 70,
            bearing: 180,

            centerCross: true,
            doubleClickZoom: false,
            baseLayer: new maptalks.TileLayer('tile', {
                urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
                subdomains: ['a', 'b', 'c', 'd'],
                attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
            })
        });

        // features to draw
        var features = [];

        buildings.forEach(function (b) {
            features = features.concat(b.features);
        });

        // the ThreeLayer to draw buildings
        var threeLayer = new maptalks.ThreeLayer('t', {
            forceRenderOnMoving: true,
            forceRenderOnRotating: true
            // animation: true
        });

        var meshs = [];
        var material = getBuildingsMaterial();
        material.vertexColors = THREE.VertexColors;

        //https://zhuanlan.zhihu.com/p/199353080
        const lineMaterial = new THREE.LineBasicMaterial({
            // 线的颜色
            color: "rgb(15,159,190)",
            transparent: true,
            linewidth: 1,
            opacity: 0.7,
            //depthTest: true,
        });
        //解决z-flighting
        lineMaterial.polygonOffset = true;
        lineMaterial.depthTest = true;
        lineMaterial.polygonOffsetFactor = 1;
        lineMaterial.polygonOffsetUnits = 1.0;


        threeLayer.prepareToDraw = function (gl, scene, camera) {

            stats = new Stats();
            stats.domElement.style.zIndex = 100;
            document.getElementById('map').appendChild(stats.domElement);

            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(0, -10, 10).normalize();
            scene.add(light);
            camera.add(new THREE.PointLight('#fff', 0.5));

            var heightPerLevel = 10;
            var polygons = features.map(f => {
                const polygon = maptalks.GeoJSON.toGeometry(f);
                var levels = f.properties.levels || 1;
                polygon.setProperties({
                    height: heightPerLevel * levels,
                });
                return polygon;
            });


            const mesh = threeLayer.toExtrudePolygons(polygons, { interactive: false, topColor: '#fff' }, material);
            meshs.push(mesh);

            var outLine = new OutLine(mesh, { interactive: false }, lineMaterial, threeLayer);
            meshs.push(outLine);
            threeLayer.addMesh(meshs);
            initGui();
            animation();
        };
        threeLayer.addTo(map);


        function getBuildingsMaterial(color = 'red') {
            const material = new THREE.MeshPhongMaterial({
                // map: texture,
                transparent: true,
                color: 'rgb(0,0,0)'
            });
            return material;
        }

        function animation() {
            // layer animation support Skipping frames
            if (!map.isInteracting()) {
                threeLayer._needsUpdate = !threeLayer._needsUpdate;
                if (threeLayer._needsUpdate) {
                    threeLayer.redraw();
                }
            }
            stats.update();
            requestAnimationFrame(animation);
        }


        function initGui() {
            var params = {
                add: true,
                color: material.color.getStyle(),
                show: true,
                opacity: 1,
                altitude: 0,
                lineColor: lineMaterial.color.getStyle(),
                lineOpacity: lineMaterial.opacity,
                animateShow: animateShow
            };
            var gui = new dat.GUI();
            gui.add(params, 'add').onChange(function () {
                if (params.add) {
                    threeLayer.addMesh(meshs);
                } else {
                    threeLayer.removeMesh(meshs);
                }
            });
            gui.addColor(params, 'color').name('building color').onChange(function () {
                material.color.set(params.color);
            });
            gui.add(params, 'opacity', 0, 1).name('building opacity').onChange(function () {
                material.opacity = params.opacity;
            });

            gui.addColor(params, 'lineColor').name('line color').onChange(function () {
                lineMaterial.color.set(params.lineColor);
            });

            gui.add(params, 'lineOpacity', 0, 1).onChange(function () {
                lineMaterial.opacity = params.lineOpacity;
            });
            gui.add(params, 'altitude', 0, 300).onChange(function () {
                meshs.forEach(function (mesh) {
                    mesh.setAltitude(params.altitude);
                });
            });
            gui.add(params, 'animateShow');
        }

        function animateShow() {
            meshs.forEach(function (mesh) {
                mesh.animateShow({
                    duration: 3000
                });
            });
        }


        //default values
        var OPTIONS = {
            altitude: 0
        };

        //https://zhuanlan.zhihu.com/p/199353080
        class OutLine extends maptalks.BaseObject {
            constructor(mesh, options, material, layer) {
                options = maptalks.Util.extend({}, OPTIONS, options, { layer });
                super();
                //Initialize internal configuration
                // https://github.com/maptalks/maptalks.three/blob/1e45f5238f500225ada1deb09b8bab18c1b52cf2/src/BaseObject.js#L135
                this._initOptions(options);

                const edges = new THREE.EdgesGeometry(mesh.getObject3d().geometry, 1);
                const lineS = new THREE.LineSegments(edges, material);
                this._createGroup();
                this.getObject3d().add(lineS);
                //Initialize internal object3d
                // https://github.com/maptalks/maptalks.three/blob/1e45f5238f500225ada1deb09b8bab18c1b52cf2/src/BaseObject.js#L140

                //set object3d position
                this.getObject3d().position.copy(mesh.getObject3d().position);
            }
        }

    </script>
</body>

</html>